var Logger = function(params) {
    this.target = (params && params.target) ? params.target : Logger.targets.ConsoleLogTarget();
    this.formatter = (params && params.formatter) ? params.formatter : Logger.formatters.SimpleLogFormatter();

    this.log = function(msg, params){
        var formattedLog = this.formatter.format(msg, params);
        this.target.log(formattedLog, params);
    };
};

/* overwrite this method for your own purposes */
Logger.register = function(object, target, formatter) {
    if (!target) {
        target = Logger.targets.ConsoleLogTarget();
    }
    
    if (!formatter) {
        formatter = Logger.formatters.DefaultLogFormatter();
    }
    
    if (typeof object == 'object') {
        object.logger = new Logger({target: target, formatter: formatter});
        object.log = function(msg) { object.logger.log(msg); };
    } else if (typeof object == 'function') {
        object.prototype.logger = new Logger({target: target, formatter: formatter});
        object.prototype.log = function(msg) { object.prototype.logger.log(msg); };
    }
};

Logger.targets = {
    AlertLogTarget: function() {
        function AlertLogTarget() {
            this.log = function(msg, params) {
                alert(msg);
            }
        }
        return new AlertLogTarget();
    },
    
    ConsoleLogTarget: function() {
        function ConsoleLogTarget() {
            this.log = function(msg, params) {
                window.console.log(msg);
            }
        }
        return new ConsoleLogTarget();
    },

    FirebugLogTarget: function() {
        function FirebugLogTarget() {
            this.log = function(msg, params) {
                window.console.log(msg);
            }
        }
        return new FirebugLogTarget();
    },

    FirefoxErrorConsoleExceptionLogTarget: function() {
        function FirefoxErrorConsoleExceptionLogTarget() {
            this.log = function(msg, params) {
                Components.utils.reportError(msg);
                dump(msg+"\n");
            }
        }
        return new FirefoxErrorConsoleExceptionLogTarget();
    },

    FirefoxSimpleErrorConsoleLogTarget: function() {
        if (!Logger.consoleService) {
            Logger.consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
        }

        function FirefoxSimpleErrorConsoleLogTarget() {
            this.log = function(msg, params) {
                Logger.consoleService.logStringMessage(msg);
                dump(msg+"\n");
            }
        }
        return new FirefoxSimpleErrorConsoleLogTarget();
    },

    FirefoxAdvancedErrorConsoleLogTarget: function() {
        if (!Logger.consoleService) {
            Logger.consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
        }

        if (!Logger.scriptErrorClass) {
            Logger.scriptErrorClass = Components.classes["@mozilla.org/scripterror;1"];
        }

        function FirefoxAdvancedErrorConsoleLogTarget() {
            this.log = function(msg, params) {
                var scriptError = Logger.scriptErrorClass.createInstance(Components.interfaces.nsIScriptError);
                scriptError.init(msg, params.sourceName, params.sourceLine, params.lineNumber, params.columnNumber, params.flags, params.category);
                Logger.consoleService.logMessage(scriptError);
                dump(msg+"\n");
            }
        }
        return new FirefoxAdvancedErrorConsoleLogTarget();
    }
};

Logger.formatters = {
    SimpleLogFormatter: function(params) {
        function SimpleLogFormatter(params) {
            this.label = (params && params.label) ? "[" + params.label + "] " : '';

            this.format = function(msg, params) {
                var date = new Date();
                var h = date.getHours();
                var m = date.getMinutes();
                var s = date.getSeconds();
                var ms = date.getMilliseconds();
                return this.label + "[" + h + ":" + m + ":" + s + ":" + ms + "] " + msg;
            };
        }
        return new SimpleLogFormatter(params);
    },

    DefaultLogFormatter: function(params) {
        function DefaultLogFormatter(params) {
            this.label = (params && params.label) ? "[" + params.label + "] " : '';
            this.format = function(msg, params) {
                var date = new Date();
                var h = date.getHours();
                var m = date.getMinutes();
                var s = date.getSeconds();
                var ms = date.getMilliseconds();

                return this.label + "[" + h + ":" + m + ":" + s + ":" + ms + "] " + doFormat(msg, 0);

                function doFormat(msg, depth) {
                    if (msg instanceof Object) {
                        var str = "{\n";
                        for (var i in msg) {
                            if (msg[i] instanceof Object) {
                                str += getIndention(depth+1) + i + ": " + doFormat(msg[i], depth+2);
                            } else {
                                str += getIndention(depth+1) + i + ": " + msg[i] + ";\n";
                            }

                        }
                        return str + getIndention(depth-1) + "}\n";
                    } else {
                        return getIndention(depth) + msg;
                    }
                }

                function getIndention(depth) {
                    if (!depth) return "";
                    var str = "";
                    while (depth > 0) {
                        str += "  ";
                        depth--;
                    }
                    return str;
                }
            };
        }
        return new DefaultLogFormatter(params);
    }
};
